from PIL import Image
import openpyxl

"""
    2021-08-18  15:02   by  CYL
    文件说明：
        将图片插入.xlsx文件中的单元格中。
"""

image_PATH = r"./circlePS.png"
#   通过pillow获取图片的大小
# pil_image = Image.open(image_PATH)
# print(pil_image.format, pil_image.size, pil_image.mode)
#   通过openpyxl向单元格中插入图片。
# the_xlsx_book = openpyxl.load_workbook(r"./123.xlsx") #   载入.xlsx文件
# the_xlsx_sheet = the_xlsx_book["Sheet1"]  #   载入.xlsx文件中的Sheet文件
# the_xlsx_img = openpyxl.drawing.image.Image(image_PATH)   #   向openpyxl中加载图片
# # the_xlsx_img.width = pil_image.size[0]  #   可有可无，本意是修改单元格的行高和列宽，但需要另外想办法。
# # the_xlsx_img.height = pil_image.size[1] #   可有可无
# the_xlsx_sheet.add_image(the_xlsx_img,'C6')   #   向C6单元格中插入图片
# the_xlsx_book.save(r"./123.xlsx") #   保存.xlsx文件

def depart_openpyxl_anchor(anchor_data:str)->(str,str,int):
    """
        2021-08-18  14:43   by  CYL
        函数名：
            depart_openpyxl_anchor()
        函数说明：
            用于解决add_image_into_xlsx()中[A0]的问题，即只能修改行高列宽一次的问题。
            该问题并不是由于openpyxl.load_workbook只能读取数据导致，因为openpyxl.load_workbook是可以含有图片的.xlsx的。
            该问题是由于re或字符串切割函数（包括自己写的）在python编译时估计被优化，然后导致字符串切割时会和修改行高列宽一同进行，导致openpyxl会出现有其余程序占用文件的问题。
        解决方法：
            字符串在add_image_into_xlsx()外部处理，再将拆分后的字符串以参数的方式传入add_image_into_xlsx()中，从而解决了线程竞争问题，确保每次都是先进行字符串处理，
            再进行xlsx的行高列宽的修改操作。
        缺陷：
            正确度可能没有使用re来得高，因为在解决add_image_into_xlsx()中[A0]的问题时，一度以为是re的问题，
            最后发现是只要是处理字符串的操作，就不要放入add_image_into_xlsx()中进行。
    :param anchor_data: .xlsx的单元格的anchor表达形式，例如'C9'代表着.xlsx中的第C列第9行。
    :return:    (anchor完整字符串,anchor列索引字符,anchor行索引数值)
    """
    xlsx_lineID_Str1DNMList = [0]
    for item in range(0,len(anchor_data)):
        if anchor_data[item] in "ABCDEFGHIJKLMNOPQRSTUVWXYZ":
            xlsx_columnID = anchor_data[item]
        elif anchor_data[item] in "0123456789":
            xlsx_lineID_Str1DNMList.append(anchor_data[item])
        else:
            pass
    xlsx_lineID_Str1DNMList.pop(0)
    xlsx_lineID = int(''.join(xlsx_lineID_Str1DNMList))
    print("(xlsx_columnID,xlsx_lineID) = ",(xlsx_columnID,xlsx_lineID))
    return (anchor_data,xlsx_columnID,xlsx_lineID)

def add_image_into_xlsx(imageFilePATH:str,departed_anchor:(str,str,int),xlsx_sheetName:str,xlsx_FilePath:str,mode:str="change cell size"):
    """
        2021-08-18  13:35   by  CYL
        函数名：
            add_image_into_xlsx()
        函数说明：
            将图片插入到.xlsx的文件中。
        使用说明：
            需要先使用depart_openpyxl_anchor()生成departed_anchor参数才可以使用本函数。
        缺陷：
            1)单元格的列宽行高的单位不一致，这个需要转换过，本程序中未进行转换。（行高无需改变，主要是列宽不能直接用图片的宽度，需要转换）
            2)pillow中得出的图片.size和openpyxl中的行高列宽是否对应还没有验证过，即pillow.size的宽是否对应openpyxl中的宽未确定。
        用法：
            if __name__ == "__main__":
                image_PATH = r"./circlePS.png"
                anchor_result = depart_openpyxl_anchor(anchor_data='B20')   #   必要步骤，用于add_image_into_xlsx()的第二参数。
                add_image_into_xlsx(imageFilePATH=image_PATH,departed_anchor=anchor_result,xlsx_sheetName='Sheet1',xlsx_FilePath=r"./123.xlsx") #   将图片插入到  .xlsx的Sheet1中的A列第8行单元格中。


    :param imageFilePATH:   源图片文件所在的路径。
    :param departed_anchor:   想要插入图片的cell位置，使用元组传入。由depart_openpyxl_anchor()生成。
    :param xlsx_sheetName:  想要插入图片的cell所在的Sheet名。
    :param xlsx_FilePath:   想要插入图片的cell所在的.xlsx的路径。
    :param mode:    1)change cell size: 以图片的大小，修改单元格列宽行高。
                    2)do not change cell size:  仅插入图片，不修改单元格列宽行高。
    :return:
    """
    if mode == "change cell size":
        pil_image = Image.open(imageFilePATH)   #获取图片的宽高，为单元格的行高和列宽修改做铺垫。
        # print("pil_image.size = ",pil_image.size)
        pil_image.close()
        the_xlsx_book = openpyxl.load_workbook(xlsx_FilePath,keep_links=True)
        the_xlsx_sheet = the_xlsx_book[xlsx_sheetName]
        the_xlsx_img = openpyxl.drawing.image.Image(imageFilePATH)
        the_xlsx_sheet.column_dimensions[departed_anchor[1]].width = pil_image.size[0] #[A0]本意是修改单元格的列宽，但只能运行一次，所以需要另外想办法。
                                                                                       # 只能运行一次的原因：the_xlsx_sheet.column_dimensions[]方括号内的东西不能在同一个def函数中生成，必须是要以传入的方式，
                                                                                       # 即处理和字符串有关的内容时，最好在使用该字符串有关的函数的外边进行，防止被编译器优化，导致线程竞争。
        the_xlsx_sheet.row_dimensions[departed_anchor[2]].height = pil_image.size[1]   #本意是修改单元格的行高，但只能运行一次，所以需要另外想办法。
        the_xlsx_sheet.add_image(the_xlsx_img,departed_anchor[0])
        the_xlsx_book.save(xlsx_FilePath)
    elif mode == "do not change cell size":
        the_xlsx_book = openpyxl.load_workbook(xlsx_FilePath,keep_links=True)
        the_xlsx_sheet = the_xlsx_book[xlsx_sheetName]
        the_xlsx_img = openpyxl.drawing.image.Image(imageFilePATH)
        the_xlsx_sheet.add_image(the_xlsx_img,departed_anchor[0])
        the_xlsx_book.save(xlsx_FilePath)
    else:
        pass

if __name__ == "__main__":
    anchor_result = depart_openpyxl_anchor(anchor_data='B20')   #   必要步骤，用于add_image_into_xlsx()的第二参数。
    add_image_into_xlsx(imageFilePATH=image_PATH,departed_anchor=anchor_result,xlsx_sheetName='Sheet1',xlsx_FilePath=r"./123.xlsx") #   将图片插入到  .xlsx的Sheet1中的A列第8行单元格中。